import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * 文件对称加密解密工具类
 * 使用AES算法对文件进行加密和解密
 */
public class FileEncryptionUtil {
    
    // AES算法名称
    private static final String ALGORITHM = "AES";
    // 默认密钥长度
    private static final int KEY_SIZE = 128;
    
    /**
     * 生成AES密钥
     * 
     * @param seed 种子
     * @return 密钥对象
     * @throws NoSuchAlgorithmException
     */
    public static Key generateKey(String seed) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        // 初始化随机数生成器
        keyGenerator.init(KEY_SIZE, new SecureRandom(seed.getBytes()));
        // 产生密钥
        SecretKey secretKey = keyGenerator.generateKey();
        // 获取密钥的二进制编码形式
        byte[] keyBytes = secretKey.getEncoded();
        // 转换为AES专用密钥
        return new SecretKeySpec(keyBytes, ALGORITHM);
    }
    
    /**
     * 初始化AES Cipher对象
     * 
     * @param key 密钥
     * @param mode 加密/解密模式 (Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE)
     * @return Cipher对象
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     */
    private static Cipher initCipher(Key key, int mode) throws NoSuchAlgorithmException, 
            NoSuchPaddingException, InvalidKeyException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(mode, key);
        return cipher;
    }
    
    /**
     * 使用CipherOutputStream方式加密文件
     * 
     * @param sourceFilePath 源文件路径
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptFile(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("源文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             BufferedInputStream bis = new BufferedInputStream(fis);
             FileOutputStream fos = new FileOutputStream(destFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            // 创建加密输出流
            CipherOutputStream cos = new CipherOutputStream(bos, cipher);
            
            byte[] buffer = new byte[1024];
            int length;
            // 读取源文件并写入加密输出流
            while ((length = bis.read(buffer)) != -1) {
                cos.write(buffer, 0, length);
            }
            cos.flush();
            cos.close();
            
            System.out.println("文件加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用CipherOutputStream方式加密输入流
     * 接收InputStream作为输入参数
     * 
     * @param inputStream 输入流
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptStream(InputStream inputStream, String destFilePath, String key) {
        File destFile = new File(destFilePath);
        
        try (BufferedInputStream bis = new BufferedInputStream(inputStream);
             FileOutputStream fos = new FileOutputStream(destFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            // 创建加密输出流
            CipherOutputStream cos = new CipherOutputStream(bos, cipher);
            
            byte[] buffer = new byte[1024];
            int length;
            // 读取输入流并写入加密输出流
            while ((length = bis.read(buffer)) != -1) {
                cos.write(buffer, 0, length);
            }
            cos.flush();
            cos.close();
            
            System.out.println("流加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用CipherOutputStream方式解密文件
     * 
     * @param sourceFilePath 加密的文件路径
     * @param destFilePath 解密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean decryptFile(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("加密文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             BufferedInputStream bis = new BufferedInputStream(fis);
             FileOutputStream fos = new FileOutputStream(destFile);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化解密器
            Cipher cipher = initCipher(secretKey, Cipher.DECRYPT_MODE);
            // 创建解密输入流
            CipherInputStream cis = new CipherInputStream(bis, cipher);
            
            byte[] buffer = new byte[1024];
            int length;
            // 读取加密文件并写入解密输出流
            while ((length = cis.read(buffer)) != -1) {
                bos.write(buffer, 0, length);
            }
            bos.flush();
            cis.close();
            
            System.out.println("文件解密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("解密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher直接处理方式加密文件
     * 适用于较小的文件，大文件可能导致内存溢出
     * 
     * @param sourceFilePath 源文件路径
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptFileWithCipher(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("源文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            
            // 读取源文件
            byte[] inputBytes = new byte[(int) sourceFile.length()];
            fis.read(inputBytes);
            
            // 加密
            byte[] outputBytes = cipher.doFinal(inputBytes);
            
            // 写入加密后的文件
            fos.write(outputBytes);
            
            System.out.println("文件加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher直接处理方式加密输入流
     * 适用于较小的输入流，大输入流可能导致内存溢出
     * 
     * @param inputStream 输入流
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptStreamWithCipher(InputStream inputStream, String destFilePath, String key) {
        File destFile = new File(destFilePath);
        
        try (FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            
            // 读取输入流
            byte[] inputBytes = inputStream.readAllBytes();
            
            // 加密
            byte[] outputBytes = cipher.doFinal(inputBytes);
            
            // 写入加密后的文件
            fos.write(outputBytes);
            
            System.out.println("流加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher直接处理方式解密文件
     * 适用于较小的文件，大文件可能导致内存溢出
     * 
     * @param sourceFilePath 加密的文件路径
     * @param destFilePath 解密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean decryptFileWithCipher(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("加密文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化解密器
            Cipher cipher = initCipher(secretKey, Cipher.DECRYPT_MODE);
            
            // 读取加密文件
            byte[] inputBytes = new byte[(int) sourceFile.length()];
            fis.read(inputBytes);
            
            // 解密
            byte[] outputBytes = cipher.doFinal(inputBytes);
            
            // 写入解密后的文件
            fos.write(outputBytes);
            
            System.out.println("文件解密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("解密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher.update方式加密大文件
     * 适用于大文件加密，避免内存溢出
     * 
     * @param sourceFilePath 源文件路径
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptLargeFile(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("源文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            byte[] encryptedData;
            
            // 分块读取并加密
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 对读取的数据块进行加密
                encryptedData = cipher.update(buffer, 0, bytesRead);
                if (encryptedData != null) {
                    fos.write(encryptedData);
                }
            }
            
            // 加密最后的数据块并写入
            encryptedData = cipher.doFinal();
            if (encryptedData != null) {
                fos.write(encryptedData);
            }
            
            System.out.println("大文件加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher.update方式加密大输入流
     * 适用于大输入流加密，避免内存溢出
     * 
     * @param inputStream 输入流
     * @param destFilePath 加密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean encryptLargeStream(InputStream inputStream, String destFilePath, String key) {
        File destFile = new File(destFilePath);
        
        try (FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化加密器
            Cipher cipher = initCipher(secretKey, Cipher.ENCRYPT_MODE);
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            byte[] encryptedData;
            
            // 分块读取并加密
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                // 对读取的数据块进行加密
                encryptedData = cipher.update(buffer, 0, bytesRead);
                if (encryptedData != null) {
                    fos.write(encryptedData);
                }
            }
            
            // 加密最后的数据块并写入
            encryptedData = cipher.doFinal();
            if (encryptedData != null) {
                fos.write(encryptedData);
            }
            
            System.out.println("大流加密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("加密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 使用Cipher.update方式解密大文件
     * 适用于大文件解密，避免内存溢出
     * 
     * @param sourceFilePath 加密的文件路径
     * @param destFilePath 解密后的文件路径
     * @param key 密钥
     * @return 是否成功
     */
    public static boolean decryptLargeFile(String sourceFilePath, String destFilePath, String key) {
        File sourceFile = new File(sourceFilePath);
        File destFile = new File(destFilePath);
        
        if (!sourceFile.exists()) {
            System.out.println("加密文件不存在");
            return false;
        }
        
        try (FileInputStream fis = new FileInputStream(sourceFile);
             FileOutputStream fos = new FileOutputStream(destFile)) {
            
            // 生成密钥
            Key secretKey = generateKey(key);
            // 初始化解密器
            Cipher cipher = initCipher(secretKey, Cipher.DECRYPT_MODE);
            
            byte[] buffer = new byte[1024];
            int bytesRead;
            byte[] decryptedData;
            
            // 分块读取并解密
            while ((bytesRead = fis.read(buffer)) != -1) {
                // 对读取的数据块进行解密
                decryptedData = cipher.update(buffer, 0, bytesRead);
                if (decryptedData != null) {
                    fos.write(decryptedData);
                }
            }
            
            // 解密最后的数据块并写入
            decryptedData = cipher.doFinal();
            if (decryptedData != null) {
                fos.write(decryptedData);
            }
            
            System.out.println("大文件解密成功，保存为: " + destFilePath);
            return true;
        } catch (Exception e) {
            System.out.println("解密过程中发生异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
}